package generate

import (
	"context"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/gin-admin/gin-admin-cli/v5/util"
	"gopkg.in/yaml.v2"
)

// Config 配置参数
type Config struct {
	Dir     string
	PkgName string
	Name    string
	Comment string
	File    string
	Modules string
}

// Exec 执行生成模块命令
func Exec(cfg Config) error {
	cmd := &Command{cfg: &cfg}
	return cmd.Exec()
}

// Command 生成命令
type Command struct {
	cfg *Config
}

func (a *Command) hasModule(m string) bool {
	if v := a.cfg.Modules; v == "" || v == "all" {
		return true
	}

	for _, s := range strings.Split(a.cfg.Modules, ",") {
		if s == m {
			return true
		}
	}

	return false
}

func (a *Command) handleError(err error, desc string) {
	if err != nil {
		fmt.Printf("%s:%s", desc, err.Error())
	}
}

// func (a *Command) structToLowerFirstMap(in) map[string]interface{} {
// 	// v := reflect.ValueOf(in)
// 	// vType := v.Type()
// 	// result := make(map[string]interface{}, v.NumField())

// 	// for i := 0; i < v.NumField(); i++ {
// 	// 	name := vType.Field(i).Name
// 	// 	result[a.lowerFirst(name)] = v.Field(i).Interface()
// 	// }

// 	// return result

// }

func (a *Command) lowerFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

// Exec 执行命令
func (a *Command) Exec() error {
	var item TplItem

	if a.cfg.File != "" {
		b, err := readFile(a.cfg.File)
		if err != nil {
			return err
		}

		err = yaml.Unmarshal(b.Bytes(), &item)
		if err != nil {
			return err
		}

	} else {
		item.StructName = a.cfg.Name
		item.Comment = a.cfg.Comment
	}

	dir, err := filepath.Abs(a.cfg.Dir)
	if err != nil {
		return err
	}

	// fmt.Println(resultDefaultData)
	// fmt.Println(dir)
	// return nil

	pkgName := a.cfg.PkgName
	ctx := context.Background()

	if a.hasModule("migration") {

		for itemI := 0; itemI < len(item.DefaultData); itemI++ {
			v := item.DefaultData[itemI]
			for itemK, itemV := range v {
				// fmt.Println(itemK, itemV)
				ToLowerUnderlinedNamerK := util.ToLowerUnderlinedNamer(itemK)
				if ToLowerUnderlinedNamerK != itemK {
					delete(v, itemK)
					v[util.ToLowerUnderlinedNamer(itemK)] = itemV
				}
			}
		}
		// fmt.Println(item.DefaultData)
		// return nil
		if len(item.DefaultData) != 0 {
			data, err := yaml.Marshal(item.DefaultData)
			// 第二个表示每行的前缀，这里不用，第三个是缩进符号，这里用tab
			if err != nil {
				return err
			}
			err = ioutil.WriteFile("../configs/initialData/"+a.cfg.File, data, 0777)
			if err != nil {
				return err
			}
		}

	}

	if a.hasModule("schema") {
		err = genSchema(ctx, pkgName, dir, item.StructName, item.Comment, item.toSchemaFields()...)
		if err != nil {
			return err
		}
	}

	if a.hasModule("dao") {
		err = genGormEntity(ctx, pkgName, dir, item.StructName, item.Comment, item.toEntityGormFields()...)
		a.handleError(err, "Generate gorm entity")

		err = genModelImplGorm(ctx, pkgName, dir, item.StructName, item.Comment)
		a.handleError(err, "Generate gorm model")

		err = insertModelInjectGorm(ctx, pkgName, dir, item.StructName)
		a.handleError(err, "Insert gorm model inject")
	}

	if a.hasModule("service") {
		err = genBllImpl(ctx, pkgName, dir, item.StructName, item.Comment, item.toSchemaFields()...)
		a.handleError(err, "Generate bll impl")

		err = insertBllInject(ctx, dir, item.StructName)
		a.handleError(err, "Insert bll inject")
	}

	if a.hasModule("api") {
		err = genAPI(ctx, pkgName, dir, item.StructName, item.Comment)
		a.handleError(err, "Generate api")

		err = insertAPIInject(ctx, dir, item.StructName)
		a.handleError(err, "Insert api inject")
	}

	if a.hasModule("mock") {
		err = genAPIMock(ctx, pkgName, dir, item.StructName, item.Comment)
		a.handleError(err, "Generate api mock")

		err = insertAPIMockInject(ctx, dir, item.StructName)
		a.handleError(err, "Insert api mock inject")
	}

	if a.hasModule("router") {
		err = insertRouterAPI(ctx, dir, item.StructName)
		a.handleError(err, "Insert router api")
		if err != nil {
			return err
		}
		err = insertRouterInject(ctx, dir, item.StructName)
		a.handleError(err, "Insert router inject")
	}

	return nil
}
